using CfgTable;
using IQIGame.Onigao.Framework;
using IQIGame.Onigao.Game;
using IQIGame.Onigao.GamePlay;
using NetProtocol.Enum;
using NetProtocol.POD;
using System;
using System.Collections.Generic;

namespace IQIGame.Onigao.Logic
{
    public partial class LevelRegion : BaseLevelEO
    {
        /// <summary>
        /// 玩家集合
        /// </summary>
        public List<LevelPlayer> players { get; private set; }
        /// <summary>
        /// 实体
        /// </summary>
        public List<LevelEntity> entities { get; private set; }
        public Dictionary<int, LevelEntity> entityMap { get; private set; }
        /// <summary>
        /// 关卡玩法配置
        /// </summary>
        public LevelGamePlayConfig gameplayConfig { get; private set; }
        public CfgRegionInstance CfgRegionInstance { get; private set; }
        public Level level { get; private set; }
        public LevelRegionTerrain terrain { get; private set; }
        /// <summary>
        /// 场景特效
        /// </summary>
        public List<int> effectIds { get; private set; }
        /// <summary>
        /// 策划自定义bgm的audio cid，该bgm优先级最高，0表示没有，-1表示全局BGM静音
        /// </summary>
        public int customBgmCid { get; set; }
        /// <summary>
        /// 是否是限制存档的模式
        /// </summary>
        public bool isRestrictedSaveMode { get; private set; }
        /// <summary>
        /// 天气id和类型的集合
        /// </summary>
        public Dictionary<int, int> customWeathers { get; private set; }
        /// <summary>
        /// mcid和实体的映射，刚开始无值，查找到一次后才会有值
        /// 实际应用中很少会用mcid查实体，所以不在初始化时映射所有实体，但条件判断中要用到，由于策划配置不可控，所以需要做缓存提高性能
        /// </summary>
        private Dictionary<int, LevelEntity> _cacheMcidEntitys = new Dictionary<int, LevelEntity>();
        private Dictionary<int, bool> _execNavEntityMap = new Dictionary<int, bool>();
        private int _aoiUpdateFrame = 0;
        /// <summary>
        /// 区域实体（Entity with EntityParamArea）列表
        /// </summary>
        private List<LevelEntity> _areaEntities = new List<LevelEntity>();
        private int _elevatorUpdateFrame = 0;
        private List<LevelElevatorMoveData> _runningElevators = new List<LevelElevatorMoveData>();
        private HashSet<LevelEntity> _checkPoints = new HashSet<LevelEntity>();
        private LevelEntity _bornPointEntity;

        public void Init(int cid, LevelRegionSavePOD regionSavePOD, Level level, LevelPlayer player)
        {
            CfgRegionInstance = TableCenter.regionInstance.Get(cid);
            this.level = level;

            var packConfig = LogicHelper.GetLevelRegionPackConfig(cid);
            gameplayConfig = packConfig.gamePlayConfig;

            players = new List<LevelPlayer>();

            terrain = new LevelRegionTerrain(cid);

            //初始化实体
            entities = new List<LevelEntity>();
            entityMap = new Dictionary<int, LevelEntity>();
            if (regionSavePOD == null || regionSavePOD.modifiedEntities.Count == 0)
            {
                for (int i = 0; i < gameplayConfig.entityConfigs.Length; i++)
                {
                    LevelEntityConfig config = gameplayConfig.entityConfigs[i];
                    CreateEntity(level.AllocateId(), new LevelEntityCtorData(config), false, true);
                }
            }
            else
            {
                List<LevelEntityPOD> modifiedEntities = regionSavePOD.modifiedEntities;
                for (int i = 0; i < modifiedEntities.Count; i++)
                {
                    LevelEntityPOD pod = modifiedEntities[i];
                    if (!pod.IsRemoved)
                    {
                        bool createByConfig = false;
                        if (gameplayConfig.entityMap.TryGetValue(pod.normal.mcid, out var config))
                        {
                            if (config.isNonpersistent)
                            {
                                createByConfig = true;
                            }
                        }
                        if (createByConfig)
                        {
                            CreateEntity(pod.id, new LevelEntityCtorData(config), false, true);
                        }
                        else
                        {
                            LevelEntity entity = CreateEntity(pod.id, new LevelEntityCtorData(pod), false, false);
                            entity.RefreshExtraFromSaveData(pod);
                            entity.StartAI();
                        }
                    }
                }
                effectIds = regionSavePOD.effectCids.Clone();
                customBgmCid = regionSavePOD.customBgmCid;
                customWeathers = regionSavePOD.customWeathers.Clone();
            }
        }

        public void Enable()
        {
            terrain.Init();
            foreach (LevelEntity entity in entities)
            {
                if (entity.isAvailable)
                {
                    entity.ActiveFunction();
                    entity.TryTriggerService(null, ServiceTriggerType.EntityBorn);
                }
            }
        }

        public void LogicOnUpdate()
        {
            if (players.Count > 0)
            {
                _aoiUpdateFrame++;

                for (int i = 0; i < entities.Count; i++)
                {
                    entities[i].LogicOnUpdate();
                }
                terrain.LogicOnUpdate();
                //降低移动的aoi刷新频率
                if (_aoiUpdateFrame >= LgLevelConst.AOIUpdateInterval * LgLevelConst.FrameRate)
                {
                    _aoiUpdateFrame = 0;
                    for (int i = 0; i < players.Count; i++)
                    {
                        LevelPlayer player = players[i];
                        RefreshPlayerAOIEntities(player, false);
                    }
                }
            }
            if (_runningElevators.Count > 0)
            {
                _elevatorUpdateFrame++;
                bool syncPos = false;
                if (_elevatorUpdateFrame >= 3)
                {
                    _elevatorUpdateFrame = 0;
                    syncPos = true;
                }
                for (int i = _runningElevators.Count - 1; i >= 0; i--)
                {
                    var data = _runningElevators[i];
                    int elapsedFrame = level.levelFrame - data.startFrame;
                    float lerpValue = Math.Min((float)elapsedFrame / data.frameDuration, 1);

                    //改变位置不能用同步模式，因为自带的同步模式(needSync:true)只支持默认移动，不支持电梯等其他模式的移动，在视图层，移动模式不同表现可能不同
                    //加上玩家正常都是通过视图层发消息来移动，同步方式也有所不同，所以最好都额外同步。
                    var oldPos = data.elevator.position;
                    data.elevator.UpdateMove(
                    Vector3Logic.Lerp(data.startPos, data.targetPos, lerpValue),
                    1,
                    false,
                    EntityMoveDirType.None);
                    if (data.carryingEntity != null)
                    {
                        var targetPos = data.carryingEntity.position + (data.elevator.position - oldPos);
                        data.carryingEntity.UpdateMove(targetPos, 1, false, EntityMoveDirType.None);
                    }

                    if (syncPos || lerpValue == 1)
                    {
                        //同步电梯位置
                        NotifySyncPosition(data.operatorPlayer, LevelMoveReason.TakeElevator,
                                 LevelEntityType.Normal, data.elevator,
                                 data.elevator.position, data.elevator.buffSpeedRatio,
                                  EntityMoveDirType.KeepRotation);
                        if (data.carryingEntity != null)
                        {
                            //同步玩家英雄位置，reason使用Move，不会同步乘坐者自己，乘坐者自己通过载具模式刷新，保持最好体验
                            NotifySyncPosition(data.operatorPlayer, LevelMoveReason.TakeElevator,
                                LevelEntityType.Hero, data.operatorPlayer.entity,
                                data.elevator.position, data.elevator.buffSpeedRatio,
                                EntityMoveDirType.KeepRotation);
                        }
                    }

                    if (lerpValue == 1)
                    {
                        StopElevator(data.elevator, data.targetFloor, data.status);
                        _runningElevators.RemoveAt(i);
                    }
                }
            }
        }

        /// <summary>
        /// 添加玩家
        /// </summary>
        /// <param name="player"></param>
        public void AddPlayer(LevelPlayer player)
        {
            players.Add(player);
        }

        /// <summary>
        /// 移除玩家
        /// </summary>
        /// <param name="player"></param>
        public void RemovePlayer(LevelPlayer player)
        {
            players.Remove(player);
        }

        public LevelPlayer GetPlayer(long playerId)
        {
            for (int i = 0; i < players.Count; i++)
            {
                LevelPlayer player = players[i];
                if (player.id == playerId)
                {
                    return player;
                }
            }
            return null;
        }

        public LevelPlayer GetUniqPlayer()
        {
            return players[0];
        }

        public void TriggerEnter(LevelPlayer player)
        {
            foreach (var entity in entities)
            {
                if (entity.isAvailable)
                {
                    entity.TryTriggerService(player, ServiceTriggerType.EnterRegion);
                }
            }
        }

        public bool InteractWithEntity(LevelPlayer player, int entityId)
        {
            if (!player.isInMainPhasing)
            {
                LogicLog.LogError("交互失败，该玩家不在主位面。");
                return false;
            }
            LevelEntity entity = GetEntityById(entityId);
            if (ValidInteract(player.entity, entity))
            {
                int requireDistance = LgLevelConst.DefaultInteractRange;
                ObstacleInfo obstacle = entity.entityConfig.obstacle;
                if (obstacle != null && !obstacle.invalid)
                {
                    requireDistance = obstacle.maxDisToCenter + LgLevelConst.HeroEntityRadius + LgLevelConst.InteractMaxBoundsGap;
                    requireDistance = requireDistance < LgLevelConst.DefaultInteractRange ? LgLevelConst.DefaultInteractRange : requireDistance;
                }
                var interactCfg = TableCenter.entityInteractType.Get(entity.interactType);
                //战斗交互类型需要获取配置里的攻击距离
                if (interactCfg.IsExtendRange)
                {
                    requireDistance += player.entity.cfgEntity.InteractExtendedRange;
                }
                float sqrDistance = (entity.position - player.entity.position).sqrMagnitude;
                if (sqrDistance <= requireDistance * requireDistance) //距离小于等于requireDistance
                {
                    return LevelServiceModule.TriggerInteractiveEntity(player, entity, ServiceTriggerType.Interact);
                }
            }
            return false;
        }

        private bool ValidInteract(LevelHeroEntity heroEntity, LevelEntity entity, bool mute = false)
        {
            if (entity == null)
            {
                LogicLog.LogWarning("交互验证失败，实体为空。");
                return false;
            }
            if (!entity.isAvailable)
            {
                LogicLog.LogWarning("交互验证失败，实体状态为休眠或移除。");
                return false;
            }
            //物理寻路模式
            if (heroEntity.navigator.isPhysical)
            {
                if (heroEntity.houseEntity != null) //英雄在房间内
                {
                    if (heroEntity.houseEntity != entity) //实体不是房间本身，需判断是否在房间内，否则默认可交互
                    {
                        //判断英雄和实体是否都在同一个房间内
                        FixedOBBRect houseRect = heroEntity.houseEntity.GetEntityOBBRect();
                        bool isEntityInHouse = houseRect.Contains(entity.position);
                        if (!isEntityInHouse)
                        {
                            if (!mute)
                                LogicLog.LogWarning("交互验证失败，英雄在房间内而实体不在。");
                            return false;
                        }
                    }
                }
                else //英雄不在房间内
                {
                    LevelEntity elevator = null;
                    if (heroEntity.isOccupied)
                    {
                        //获取当前电梯或电梯门实体（同一时间只会是其一）
                        elevator = heroEntity.GetInteractingEntity(EntityOccupationStatus.UseElevator) as LevelEntity;
                    }
                    if (elevator != null && IsRunningElevator(elevator.id))
                    {
                        if (!mute)
                            LogicLog.LogWarning("交互验证失败，电梯还未停止。");
                        return false;
                    }
                }
            }
            else //非物理寻路模式
            {
                if (heroEntity.isOccupied || entity.isOccupied)
                {
                    //不合法，报错
                    if (!mute)
                    {
                        LevelEntity targetEntity = heroEntity.isOccupied ? heroEntity.GetAnyInteractingEntity() as LevelEntity : entity;
                        string entityName = string.Empty;
                        if (targetEntity.entityConfig != null)
                        {
                            if (targetEntity.entityConfig.infoCid != 0)
                            {
                                CfgLevelEntityInfo cfgInfo = TableCenter.levelEntityInfo.Get(targetEntity.entityConfig.infoCid);
                                entityName = cfgInfo.Name;
                            }
                            else
                            {
                                entityName = targetEntity.entityConfig.id.ToString();
                            }
                        }
                        LogicLog.LogWarning(heroEntity.isOccupied ? $"交互验证失败，英雄被实体【{entityName}】占用" : $"交互验证失败，实体【{entityName}】被占用");
                    }
                    return false;
                }
            }
            return true;
        }

        /// <summary>
        /// 移动触发（针对单个实体）
        /// </summary>
        /// <param name="player"></param>
        /// <param name="entity"></param>
        /// <returns>是否触发成功</returns>
        public bool MoveTrigger(LevelPlayer player, LevelEntity entity)
        {
            if (entity.isAvailable && ValidInteract(player.entity, entity, true))
            {
                bool hasSuccess = entity.TryTriggerService(player, ServiceTriggerType.MoveInRange);
                if (hasSuccess) //只要交互触发成功，则结束循环，因为交互一次只能和一个实体进行，不能同时和多个实体交互。
                {
                    return true;
                }
            }
            return false;
        }

        public bool Move(LevelPlayer player, Vector3Logic position, bool needSync = true)
        {
            if (!player.isInMainPhasing)
            {
                LogicLog.LogError("移动同步失败，该玩家不在主位面。");
                return false;
            }
            LevelHeroEntity heroEntity = player.entity;
            Vector3Logic lastPos = heroEntity.position;
            bool isOK = player.ClientMoveHero(position);
            if (isOK)
            {
                //将这一次的实体距离数据刷到上一次，然后再移动。
                //这么做是为了为一些需要判断英雄移动历史的功能提供数据（如是否从某范围内刚好离开）
                heroEntity.EntityDisNowToPrev();

                //尝试触发移动触发器
                List<LevelEntity> entitiesInRange = ListPool<LevelEntity>.Get(); //using entities
                {
                    terrain.QuadSearchEntity(entitiesInRange, heroEntity.position);
                    bool hasEntityTriggered = false;
                    for (int i = 0; i < entitiesInRange.Count; i++)
                    {
                        LevelEntity entity = entitiesInRange[i];
                        float sqrDistance = (entity.position - heroEntity.position).sqrMagnitude;
                        if (sqrDistance <= LgLevelConst.HeroQuadQueryHalfSize * LgLevelConst.HeroQuadQueryHalfSize) //不能超过查询范围
                        {
                            heroEntity.UpdateEntityDisForTrig(entity.id, sqrDistance); //更新这一次的实体距离数据
                        }
                        if (!hasEntityTriggered) //一次只触发一个实体的服务
                        {
                            hasEntityTriggered = MoveTrigger(player, entity); //移动触发
                        }
                    }
                    ListPool<LevelEntity>.Put(entitiesInRange); //release entities
                }

                CheckAreaChange(player);
            }
            if (needSync)
            {
                if (isOK)
                {
                    NotifySyncPosition(player, LevelMoveReason.Move, LevelEntityType.Hero, heroEntity, heroEntity.position, heroEntity.buffSpeedRatio, EntityMoveDirType.KeepVertical);
                }
                else
                {
                    NotifySyncPosition(player, LevelMoveReason.MoveFail, LevelEntityType.Hero, heroEntity, lastPos, heroEntity.buffSpeedRatio, EntityMoveDirType.KeepVertical);
                }
            }
            return isOK;
        }

        private void CheckAreaChange(LevelPlayer player)
        {
            if (_areaEntities.Count > 0)
            {
                LevelHeroEntity heroEntity = player.entity;
                //检查是否进入或离开区域
                //一个地区的区域不会多，且可能会很大，甚至大于LgLevelConst.HeroQuadQueryHalfSize，所以单独判断是最高效的
                var lastAreaEntity = heroEntity.areaEntity;
                int curAreaEntityId = 0;
                LevelEntity curAreaEntity = null;
                foreach (LevelEntity areaEntity in _areaEntities)
                {
                    if (areaEntity.isAvailable && CheckIntersect(heroEntity, areaEntity))
                    {
                        curAreaEntityId = areaEntity.id;
                        curAreaEntity = areaEntity;
                        break;
                    }
                }
                heroEntity.SetAreaEntityId(curAreaEntityId);
                //进入区域
                if (heroEntity.areaEntity != null && heroEntity.areaEntity != lastAreaEntity)
                {
                    TrigValueTypeParam param = new TrigValueTypeParam();
                    param.param1 = 1;
                    curAreaEntity.TryTriggerService(player, ServiceTriggerType.EnterOrLeaveArea, null, param);
                }
                else if (lastAreaEntity != null && heroEntity.areaEntity != lastAreaEntity)
                {
                    TrigValueTypeParam param = new TrigValueTypeParam();
                    param.param1 = 0;
                    lastAreaEntity.TryTriggerService(player, ServiceTriggerType.EnterOrLeaveArea, null, param);
                }
            }
        }

        public bool GetTeleportPosByEntityId(int id, out Vector3Logic position)
        {
            LevelEntity entity = GetEntityById(id);
            if (entity == null)
            {
                LogicLog.LogError($"传送失败，找不到实体，id：{id}");
                position = default;
                return false;
            }
            return LgLevelEntityUtil.GetTeleportPos(entity, out position);
        }

        public bool GetTeleportPosByEntityTag(int portalTagOrMcid, out Vector3Logic position)
        {
            LevelEntity entity = GetEntityByTagOrMcid(portalTagOrMcid);
            if (entity == null)
            {
                LogicLog.LogError($"传送失败，找不到实体，tagOrMcid：{portalTagOrMcid}");
                position = default;
                return false;
            }
            return LgLevelEntityUtil.GetTeleportPos(entity, out position);
        }

        /// <summary>
        /// 获取离玩家最近的存档点，如果没有则返回出生点
        /// </summary>
        /// <param name="player"></param>
        /// <returns></returns>
        public LevelEntity GetNearestPortal(LevelPlayer player)
        {
            if (_checkPoints.Count == 0)
            {
                return _bornPointEntity;
            }
            float minDis = int.MaxValue;
            LevelEntity nearestCheckPoint = null;
            foreach (var checkPoint in _checkPoints)
            {
                if (!checkPoint.isCheckPointActive)
                {
                    continue;
                }
                var sqrDis = (checkPoint.position - player.entity.position).sqrMagnitude;
                if (sqrDis < minDis)
                {
                    minDis = sqrDis;
                    nearestCheckPoint = checkPoint;
                }
            }
            if (nearestCheckPoint != null)
            {
                return nearestCheckPoint;
            }
            return _bornPointEntity;
        }

        public bool TeleportAndNotify(LevelPlayer player, Vector3Logic position)
        {
            if (!player.isInMainPhasing)
            {
                LogicLog.LogError("传送失败，该玩家不在主位面");
                return false;
            }
            LevelHeroEntity heroEntity = player.entity;
            Vector3Logic lastPos = heroEntity.position;
            if (Teleport(player, position))
            {

                NotifySyncPosition(player, LevelMoveReason.Teleport, LevelEntityType.Hero, heroEntity, position, 1, EntityMoveDirType.KeepVertical);
                return true;
            }
            NotifySyncPosition(player, LevelMoveReason.MoveFail, LevelEntityType.Hero, heroEntity, lastPos, 1, EntityMoveDirType.KeepVertical);
            return false;
        }

        public bool Teleport(LevelPlayer player, Vector3Logic position)
        {
            bool isSuccess = player.entity.Teleport(position);
            if (isSuccess)
            {
                CheckAreaChange(player);
            }
            return isSuccess;
        }

        /// <summary>
        /// 创建实体
        /// </summary>
        /// <param name="id"></param>
        /// <param name="data"></param>
        /// <param name="isActivateNow">是否立刻激活，如果是关卡创建时创建的，通常延后到Enable时再激活，如果是关卡运行过程中创建的，通常立刻激活</param>
        /// <param name="isActiveBehaviour">是否要开启行为（行为树或状态机）</param>
        /// <returns></returns>
        public LevelEntity CreateEntity(int id, LevelEntityCtorData data, bool isActivateNow, bool isActiveBehaviour)
        {
            LevelEntity entity = ClassPool.Get<LevelEntity>();
            entity.Init(id, this, data);
            entities.Add(entity);
            entityMap.Add(entity.id, entity);
            bool isAreaEntity = entity.HasDynamicParam(EntityDynamicParamType.Area);
            if (isAreaEntity)
            {
                _areaEntities.Add(entity);
            }
            if (isActivateNow) //立刻激活
            {
                if (entity.isAvailable)
                {
                    entity.ActiveFunction();
                    entity.TryTriggerService(null, ServiceTriggerType.EntityBorn);
                }
                for (int i = 0; i < players.Count; i++)
                {
                    LevelHeroEntity heroEntity = players[i].entity;
                    float sqrDistance = (heroEntity.position - entity.position).sqrMagnitude;
                    //进入了aoi范围，需添加到aoi列表中。
                    //如果是区域实体，不添加到列表中，该类型实体会特殊处理，在第一次更新aoi列表和清空aoi列表时，恒定加入到更新的实体中去。也就是说区域实体是不在aoi列表中的恒定aoi实体
                    if (!isAreaEntity && sqrDistance < LgLevelConst.SqrAOIRadius)
                    {
                        heroEntity.AddAOIEntity(entity);
                    }
                }
            }
            if (isActiveBehaviour)
            {
                entity.StartAI();
            }
            return entity;
        }

        public void AddEntityObstacle(LevelEntity entity)
        {
            ObstacleInfo obstacle = entity.entityConfig.obstacle;
            if (obstacle != null && !obstacle.invalid && !obstacle.isClient)
            {
                entity.ObstacleId = terrain.AddObstacle(entity.position, entity.dir, entity.entityConfig.obstacle, LgLevelConst.EntitySpeed);
            }
        }

        public void RemoveEntityObstacle(LevelEntity entity)
        {
            if (entity.ObstacleId != 0)
            {
                terrain.RemoveObstacle(entity.entityConfig.obstacle.isStatic, entity.ObstacleId);
            }
        }

        public void ChangeEntityAgentSpeed(LevelEntity entity, int speed, int acceleration)
        {
            if (entity.ObstacleId != 0)
            {
                terrain.ChangeAgentSpeed(entity.ObstacleId, speed, acceleration);
            }
        }

        /// <summary>
        /// 缓存特殊实体（目前只有存档点和出生点，因为这两种点可能会在复活时遍历获取最近的）
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="speTag"></param>
        /// <param name="isActive"></param>
        public void CacheSpeEntity(LevelEntity entity, LevelEntityTag speTag, bool isActive)
        {
            switch (speTag)
            {
                case LevelEntityTag.DefaultBorn:
                    _bornPointEntity = isActive ? entity : null;
                    break;
                case LevelEntityTag.CheckPoint:
                    if (isActive)
                    {
                        _checkPoints.Add(entity);
                    }
                    else
                    {
                        _checkPoints.Remove(entity);
                    }
                    break;
                default:
                    break;
            }
        }

        public void RemoveEntitys(List<LevelEntity> entities)
        {
            foreach (var entity in entities)
            {
                RemoveEntity(entity);
            }
        }

        public void RemoveEntity(LevelEntity entity)
        {
            //不要从集合中移除
            entity.OnRemove();
            if (entity.ObstacleId != 0)
            {
                RemoveEntityObstacle(entity);
                terrain.RemoveFromQuadTree(entity);
                if (entity.HasTriggerRect())
                {
                    terrain.RemoveFromBvhTree(entity);
                }
            }
        }

        /// <summary>
        /// 根据tag或mcid获取第一个实体
        /// 由于tag和mcid从设计上不可能冲突，所以会自动判断通过什么获取
        /// </summary>
        /// <param name="tagOrMcid"></param>
        public LevelEntity GetEntityByTagOrMcid(int tagOrMcid, bool includeInactive = false, bool includeRemoved = false)
        {
            if (LgLevelEntityUtil.IsTag(tagOrMcid))
            {
                return GetEntityByTag((ushort)tagOrMcid, includeInactive, includeRemoved);
            }
            else
            {
                return GetEntityByMcid(tagOrMcid, includeInactive, includeRemoved);
            }
        }

        /// <summary>
        /// 根据tag获取单个实体
        /// </summary>
        /// <param name="tag"></param>
        /// <returns></returns>
        private LevelEntity GetEntityByTag(ushort tag, bool includeInactive, bool includeRemoved)
        {
            foreach (LevelEntity entity in entities)
            {
                if (entity.tags.Contains(tag) && (includeInactive || entity.isActive) && (includeRemoved || !entity.isRemoved))
                {
                    return entity;
                }
            }
            return null;
        }

        /// <summary>
        /// 根据id获取实体
        /// </summary>
        /// <param name="tag"></param>
        public LevelEntity GetEntityById(int id, bool includeInactive = false, bool includeRemoved = false)
        {
            if (entityMap.TryGetValue(id, out var entity))
            {
                if ((includeInactive || entity.isActive) && (includeRemoved || !entity.isRemoved))
                    return entity;
            }
            return null;
        }

        /// <summary>
        /// 根据tag或mcid获取第一个实体
        /// 由于tag和mcid在设计上肯定不会重复，所以会自动判断通过什么获取
        /// </summary>
        /// <param name="tagOrMcid"></param>
        public void GetEntitiesByTagOrMcid(int tagOrMcid, List<LevelEntity> resultList, bool includeInactive = false, bool includeRemoved = false)
        {
            if (LgLevelEntityUtil.IsTag(tagOrMcid))
            {
                GetEntitiesByTag((ushort)tagOrMcid, resultList, includeInactive, includeRemoved);
            }
            else
            {
                GetEntitiesByMcid(tagOrMcid, resultList, includeInactive, includeRemoved);
            }
        }

        private void GetEntitiesByTag(ushort tag, List<LevelEntity> resultList, bool includeInactive, bool includeRemoved)
        {
            foreach (LevelEntity entity in entities)
            {
                if (entity.tags.Contains(tag) && (includeInactive || entity.isActive) && (includeRemoved || !entity.isRemoved))
                {
                    resultList.Add(entity);
                }
            }
        }

        public void GetAllEntities(List<LevelEntity> resultList)
        {
            foreach (LevelEntity entity in entities)
            {
                resultList.Add(entity);
            }
        }

        /// <summary>
        /// 根据配置id获取实体
        /// </summary>
        /// <param name="mcid"></param>
        public LevelEntity GetEntityByMcid(int mcid, bool includeInactive = false, bool includeRemoved = false)
        {
            for (int i = 0; i < entities.Count; i++)
            {
                LevelEntity entity = entities[i];
                if (entity.entityConfig != null && entity.entityConfig.id == mcid && (includeInactive || entity.isActive) && (includeRemoved || !entity.isRemoved))
                {
                    return entity;
                }
            }
            return null;
        }

        /// <summary>
        /// 根据配置id获取实体
        /// </summary>
        /// <param name="mcid"></param>
        public void GetEntitiesByMcid(int mcid, List<LevelEntity> resultEntities, bool includeInactive = false, bool includeRemoved = false)
        {
            for (int i = 0; i < entities.Count; i++)
            {
                LevelEntity entity = entities[i];
                if (entity.entityConfig != null && entity.entityConfig.id == mcid && (includeInactive || entity.isActive) && (includeRemoved || !entity.isRemoved))
                {
                    resultEntities.Add(entity);
                }
            }
        }

        /// <summary>
        /// 英雄跳跃
        /// </summary>
        /// <param name="player"></param>
        /// <returns>ErrorCode</returns>
        public int HeroJump(LevelPlayer player, int entityId, float dirX, float dirZ)
        {
            LevelEntity entity = GetEntityById(entityId);

            if (entity == null)
            {
                return ErrorCode.EntityNotFound;
            }

            LevelHeroEntity heroEntity = player.entity;
            bool isIntersecting = CheckIntersect(heroEntity, entity);

            if (!isIntersecting)
            {
                return ErrorCode.NotInRange;
            }

            EntityParamLedgeData ledgeData = entity.GetDynamicParam(EntityDynamicParamType.Ledge) as EntityParamLedgeData;

            if (ledgeData == null)
            {
                return ErrorCode.InvalidEntity;
            }

            Vector3Logic assumedDroppoint = heroEntity.position + new Vector3Logic(dirX, 0, dirZ) * ledgeData.jumpDistance;
            assumedDroppoint.y = assumedDroppoint.y + ledgeData.jumpHeight;

            if (Teleport(player, assumedDroppoint))
            {
                return ErrorCode.SUCCESS;
            }

            return ErrorCode.TargetPosInvalid;
        }

        /// <summary>
        /// 检查英雄和实体是否相交
        /// </summary>
        /// <param name="heroEntity"></param>
        /// <param name="entity"></param>
        /// <returns></returns>
        public bool CheckIntersect(LevelHeroEntity heroEntity, LevelEntity entity)
        {
            FixedOBBRect heroRect = new FixedOBBRect(heroEntity.position, heroEntity.obbSize, 0);
            FixedOBBRect entityRect = entity.GetEntityOBBRect();

            return heroRect.IsIntersecting(entityRect);
        }

        /// <summary>
        /// 获取所有功能触发器与Hero在3D空间相交（会计算y值）的实体
        /// </summary>
        /// <param name="player"></param>
        /// <param name="resultList"></param>
        private void GetTrigRectIntersectEntities(LevelPlayer player, List<LevelEntity> resultList, bool checkEntityTriggerRectHeight = true)
        {
            LevelHeroEntity heroEntity = player.entity;
            List<LevelEntity> tempEntities = ListPool<LevelEntity>.Get();
            {
                terrain.BvhSearchEntity(tempEntities, heroEntity.position, heroEntity.obbSize, 0);
                foreach (LevelEntity entity in tempEntities)
                {
                    if (CheckIntersect(heroEntity, entity) && (!checkEntityTriggerRectHeight || entity.IsInTriggerRectHeightRange(heroEntity.position)))
                    {
                        resultList.Add(entity);
                    }
                }
                ListPool<LevelEntity>.Put(tempEntities);
            }
        }

        /// <summary>
        /// 获取所有功能触发器与Hero在3D空间相交（会计算y值）的实体
        /// </summary>
        /// <param name="player"></param>
        /// <param name="limitEntityIds">只在这些实体里查询，如果为空则查询所有实体</param>
        /// <param name="resultList"></param>
        private void GetTrigRectIntersectEntities(LevelPlayer player, List<int> limitEntityIds, List<LevelEntity> resultList)
        {
            LevelHeroEntity heroEntity = player.entity;
            foreach (int id in limitEntityIds)
            {
                LevelEntity entity = GetEntityById(id);
                if (CheckIntersect(heroEntity, entity) && entity.IsInTriggerRectHeightRange(heroEntity.position))
                {
                    resultList.Add(entity);
                }
            }
        }

        public int SwitchPhysicNavMode(LevelPlayer player, bool isPhysical, Vector3Logic exitPhysicPos)
        {
            LevelHeroEntity heroEntity = player.entity;
            LevelEntity physicRelativeEntity;
            if (isPhysical) //进入物理寻路模式
            {
                //房间属于特殊占用状态，英雄可能会与房间内的其他实体交互
                if (heroEntity.isOccupied || heroEntity.houseEntity != null)
                {
                    return ErrorCode.HeroOccupied;
                }
                LevelEntity targetEntity = null;
                EntityDynamicParamType entityType = EntityDynamicParamType.None;
                List<LevelEntity> intersectings = ListPool<LevelEntity>.Get();
                {
                    //通过视图层给的列表获取所有相交的实体（校验相交）
                    GetTrigRectIntersectEntities(player, intersectings);
                    //获取离英雄最近的
                    float minOffsetY = float.MaxValue;
                    foreach (LevelEntity tempEntity in intersectings)
                    {
                        EntityDynamicParamType tempType = tempEntity.GetPhysicType();
                        if (tempType == EntityDynamicParamType.None)
                        {
                            continue;
                        }
                        float offsetY = Math.Abs(tempEntity.position.y - heroEntity.position.y);
                        if (offsetY > LgLevelConst.TriggerRectYThreshold)
                        {
                            continue;
                        }
                        if (offsetY < minOffsetY)
                        {
                            minOffsetY = offsetY;
                            entityType = tempType;
                            targetEntity = tempEntity;
                        }
                    }
                    ListPool<LevelEntity>.Put(intersectings);
                }
                //如果找不到实体或实体距离英雄高度差太大则返回
                if (targetEntity == null)
                {
                    return ErrorCode.NotInRange;
                }
                //纪录进入物理区域的位置
                heroEntity.enterPhysicAreaPos = heroEntity.position;
                //处理具体的切换逻辑，不用类型的触发实体逻辑可能不用
                heroEntity.houseEntity = targetEntity;
                physicRelativeEntity = targetEntity;
            }
            else //退出物理寻路模式
            {
                //房间属于特殊占用状态，英雄可能会与房间内的其他实体交互
                if (!heroEntity.isOccupied && heroEntity.houseEntity == null)
                {
                    return ErrorCode.InvalidStatus;
                }
                if (heroEntity.houseEntity != null)
                {
                    //先检查英雄位置是否合法，合法位置参考点为英雄进入房间时的位置（房间功能目前要求只能从同一个门进出）
                    if (ValidPositionFromPhysicToRecast(player, exitPhysicPos, heroEntity.enterPhysicAreaPos))
                    {
                        physicRelativeEntity = heroEntity.houseEntity;
                        heroEntity.houseEntity = null;
                        heroEntity.enterPhysicAreaPos = default;
                    }
                    else
                    {
                        return ErrorCode.NotInRange;
                    }
                }
                else
                {
                    LogicLog.LogError($"切换网格寻路模式出错：当前未处于物理寻路模式特殊实体占用状态。");
                    return ErrorCode.InvalidStatus;
                }
            }

            player.entity.navigator.SwitchPhysicNavMode(physicRelativeEntity);
            //应策划需求，ServiceTriggerType.SwitchPhysicalArea 写死固定触发几种特定实体的服务
            TrigValueTypeParam parameter = new TrigValueTypeParam();
            parameter.param1 = isPhysical ? 1 : 0; //trigger param: isEnter
            physicRelativeEntity.TryTriggerService(player, ServiceTriggerType.SwitchPhysicalArea, null, parameter);
            if (player.entityFollowingMe != null)
            {
                player.entityFollowingMe.TryTriggerService(player, ServiceTriggerType.SwitchPhysicalArea, null, parameter);
            }

            return ErrorCode.SUCCESS;
        }

        /// <summary>
        /// 检验从物理寻路切换回网格寻路时，视图层发送过来的英雄位置是否合法
        /// </summary>
        /// <param name="clientHeroPos">视图层发送过来的英雄位置</param>
        /// <param name="referencePos">不同模式下逻辑层内部用于参考的合法位置</param>
        /// <returns></returns>
        private bool ValidPositionFromPhysicToRecast(LevelPlayer player, Vector3Logic clientHeroPos, Vector3Logic referencePos)
        {
            Vector3Logic distance = clientHeroPos - referencePos;
            if (distance.sqrMagnitude > LgLevelConst.ExitPhysicrValidSqrDistance)
            {
                LogicLog.LogError($"切换网格寻路模式出错：英雄位置离合法位置太远。合法参考点位置:{referencePos / LgLevelConst.DistanceUnitRatio}。");
                return false;
            }
            if (!terrain.TryMove(clientHeroPos, referencePos, out Vector3Logic realEndPos))
            {
                LogicLog.LogError($"切换网格寻路模式出错：英雄位置无法行进到合法位置。合法参考点位置:{referencePos / LgLevelConst.DistanceUnitRatio}。");
                return false;
            }
            Teleport(player, clientHeroPos);
            return true;
        }

        public int TriggerEntityState(LevelPlayer player, int entityId, int state)
        {
            if (entityMap.TryGetValue(entityId, out LevelEntity entity))
            {
                TrigValueTypeParam parameter = new TrigValueTypeParam();
                parameter.param1 = state;
                entity.TryTriggerService(player, ServiceTriggerType.EntityState, null, parameter);
                return ErrorCode.SUCCESS;
            }
            LogicLog.LogError("触发实体状态错误。没有找到实体：" + entityId);
            return ErrorCode.EntityNotFound;
        }

        /// <summary>
        /// 重置指定实体
        /// </summary>
        /// <param name="player"></param>
        /// <param name="entity"></param>
        public void ResetEntities(LevelPlayer player, List<LevelEntity> entities)
        {
            foreach (var entity in entities)
            {
                entity.Reset();
            }
            UpdateEntities(player, entities);
        }

        public void StartElevator(LevelPlayer player, LevelEntity elevator, int targetFloor, bool isRideMode)
        {
            elevator.SetInteractType(1, false);
            var heroEntity = player.entity;
            if (isRideMode)
            {
                //乘坐模式，如果英雄不在物理区域内则直接拉到最近的位置
                Vector3Logic heroPos = heroEntity.position;
                if (!LgLevelEntityUtil.XYCenterRectContains(heroPos, elevator.position, elevator.triggerRect))
                {
                    RectInt eleRect = elevator.triggerRect;
                    RectInt smallerRect = new RectInt(eleRect.X, eleRect.Y, (int)(eleRect.Width * 0.95f), (int)(eleRect.Height * 0.95f));
                    Vector3Logic newPos = LgLevelEntityUtil.GetNearestPosFromXYCenterRect(heroPos, elevator.position, smallerRect);
                    heroEntity.UpdateMove(newPos, 1, false, EntityMoveDirType.None);
                    NotifySyncPosition(player, LevelMoveReason.MoveFail, LevelEntityType.Hero, heroEntity, newPos, heroEntity.buffSpeedRatio, EntityMoveDirType.KeepVertical);
                }
                heroEntity.OccupiedBy(EntityOccupationStatus.UseElevator, 0, elevator);
            }
            EntityOccupationStatus status = isRideMode ? EntityOccupationStatus.UseElevator : EntityOccupationStatus.CallElevator;
            elevator.OccupiedBy(status, 0, heroEntity);

            EntityParamElevator elevatorParam = elevator.GetDynamicParam(EntityDynamicParamType.Elevator) as EntityParamElevator;
            Vector3Logic targetPos = elevatorParam.floors[targetFloor].position;
            var cfgLevelEntity = TableCenter.physicalEntity.Get(elevator.entityConfig.entityCid);
            int frameDuration = (int)((targetPos - elevator.position).magnitude / cfgLevelEntity.DefaultSpeed * LgLevelConst.FrameRate);

            _runningElevators.Add(new LevelElevatorMoveData(player, elevator, targetFloor, status, level.levelFrame, frameDuration, elevator.position, targetPos, isRideMode ? heroEntity : null));

            if (isRideMode)
            {
                player.entity.navigator.SwitchPhysicNavMode(elevator);
                UpdateHeroCarrier(player, elevator, false);
            }
            NotifyActivateElevator(player, elevator, targetFloor, isRideMode, true);
        }

        /// <summary>
        /// 电梯到达
        /// </summary>
        /// <param name="elevator"></param>
        /// <param name="targetFloor"></param>
        /// <returns></returns>
        public int StopElevator(LevelEntity elevator, int targetFloor, EntityOccupationStatus occupationStatus)
        {
            EntityPropElevator elevatorProp = elevator.GetDynamicProperty(EntityDynamicPropertyType.Elevator) as EntityPropElevator;
            elevatorProp.curFloor = targetFloor;

            //清除电梯占用
            LevelHeroEntity heroEntity = elevator.GetInteractingEntity(occupationStatus) as LevelHeroEntity;
            LevelPlayer player = heroEntity.player;

            elevator.EndOccupationAction(player, occupationStatus);
            //乘坐模式下，清除hero的占用
            bool isRideMode = occupationStatus == EntityOccupationStatus.UseElevator;
            if (isRideMode)
            {
                player.entity.navigator.SwitchPhysicNavMode(null);
                heroEntity.EndOccupationAction(player, EntityOccupationStatus.UseElevator);
                UpdateHeroCarrier(player, elevator, true);
            }
            NotifyActivateElevator(player, elevator, targetFloor, isRideMode, false);

            elevator.RecoverInteractType(false);
            TrigValueTypeParam parameter = new TrigValueTypeParam();
            parameter.param1 = targetFloor;
            elevator.TryTriggerService(player, ServiceTriggerType.ElevatorArrive, null, parameter);

            return ErrorCode.SUCCESS;
        }

        public bool IsRunningElevator(int entityId)
        {
            foreach (var data in _runningElevators)
            {
                if (data.elevator.id == entityId)
                {
                    return true;
                }
            }
            return false;
        }

        public void ChangeSceneEffect(int effectCid, bool isAdd)
        {
            if (effectCid <= 0)
            {
                return;
            }
            if (isAdd)
            {
                effectIds = effectIds ?? new List<int>();
                effectIds.Add(effectCid);
            }
            else if (effectIds != null)
            {
                effectIds.Remove(effectCid);
            }
        }

        public void SetWeather(int id, int type)
        {
            if (!gameplayConfig.weatherTypes.ContainsKey(id))
            {
                LogicLog.LogError($"设置天气失败，不支持的id：{id}");
            }
            int oldType = GetWeather(id);
            if (oldType == type)
            {
                return;
            }
            customWeathers = customWeathers ?? new Dictionary<int, int>();
            customWeathers[id] = type;
            NotifyChangeWeather(id, type);
        }

        public int GetWeather(int id)
        {
            int weatherType;
            if (customWeathers != null && customWeathers.TryGetValue(id, out weatherType))
            {
                return weatherType;
            }
            if (gameplayConfig.weatherTypes.TryGetValue(id, out weatherType))
            {
                return weatherType;
            }
            return 0;
        }

        public bool CanSave()
        {
            if (isRestrictedSaveMode)
            {
                LogicLog.LogWarning("无法存档，当前处于限制存档模式。");
                return false;
            }
            if (_execNavEntityMap.Count > 0)
            {
                LogicLog.LogWarning("无法存档，当前关卡有npc正在寻路。");
                return false;
            }
            for (int i = 0; i < players.Count; i++)
            {
                if (players[i].entity.isOccupied)
                {
                    LogicLog.LogWarning("无法存档，有玩家处于占用状态。");
                    return false;
                }
            }
            return true;
        }

        public void RestrictSave()
        {
            isRestrictedSaveMode = true;
        }

        public void UnrestrictSave()
        {
            isRestrictedSaveMode = false;
        }

        public void RecordNavEntity(LevelEntity entity)
        {
            _execNavEntityMap.Add(entity.id, true);
        }

        public void RemoveNavEntityRecord(LevelEntity entity)
        {
            _execNavEntityMap.Remove(entity.id);
        }

        public void SwitchEntityBehaviour(bool isPause)
        {
            foreach (var entity in entities)
            {
                entity.SwitchEntityBehaviour(isPause);
            }
        }

        /// <summary>
        /// 道具提交确认
        /// </summary>
        /// <param name="player"></param>
        /// <param name="submittedItemList"></param>
        /// <returns>是否正确执行，注意提交失败是业务失败，但也算正确执行</returns>
        public bool ConfirmItemSubmit(LevelPlayer player, List<long> submittedItemList, out bool isSuccess)
        {
            int submitListCount = submittedItemList == null ? 0 : submittedItemList.Count;
            LevelEntity triggerEntity = (LevelEntity)player.entity.GetInteractingEntity(EntityOccupationStatus.ItemSubmit);
            isSuccess = false;
            //客户端发送长度为0认为取消
            if (submitListCount == 0)
            {
                ReleaseItemSubmitOccupation(player, triggerEntity);
                return true;
            }
            if (!player.entity.TryGetOccupation(EntityOccupationStatus.ItemSubmit, out int cid))
            {
                LogicLog.LogError("玩家实体不处于道具提交状态");
                return false;
            }
            var cfgItemSubmit = TableCenter.levelItemSubmit.Get(cid);
            if (cfgItemSubmit.JudgeID.Count == 0)
            {
                LogicLog.LogError($"道具提交失败，submit cid: {cid}没有配置judge。");
                return false;
            }
            var sampleJudgeCfg = TableCenter.levelItemJudge.Get(cfgItemSubmit.JudgeID[0]);
            int judgeCount = sampleJudgeCfg.ItemJudge.Count;
            if (judgeCount != submitListCount)
            {
                LogicLog.LogError($"道具提交失败，submit cid: {cid}，judge数量和提交的数量不匹配，judge数量: {judgeCount}, 提交的数量: {submitListCount}");
                return false;
            }
            var playerBasicItems = player.logicPlayer.basicItems;
            var playerItems = player.logicPlayer.items;
            HashSet<int> matchItemCids = new HashSet<int>();
            CfgLevelItemJudge matchedJudge = null;
            //判断所有judge有没有一个是匹配的
            foreach (int judgeCid in cfgItemSubmit.JudgeID)
            {
                var cfgItemJudge = TableCenter.levelItemJudge.Get(judgeCid);
                matchItemCids.Clear();
                matchItemCids.AddRange(cfgItemJudge.ItemJudge);
                bool isMatch = true;
                //判断每一个judge的所有item是否匹配
                for (int i = 0; i < cfgItemJudge.ItemJudge.Count; i++)
                {
                    int requireItemCid = cfgItemJudge.ItemJudge[i];
                    if (requireItemCid <= 0)
                    {
                        LogicLog.LogError($"道具提交失。submit cid: {cid}, ItemJudge不能为0。");
                        return false;
                    }
                    if (!cfgItemSubmit.AllowItem.TryGetValue(requireItemCid, out int requireItemNum) || requireItemNum <= 0)
                    {
                        LogicLog.LogError($"道具提交失败，judge配置的cid在Allow里找不到或需求数量为0。submit cid: {cid}, error item cid: {requireItemCid}");
                        return false;
                    }
                    long submitItemId = submittedItemList[i];
                    int submitItemCid;
                    int submitItemNum = 0;
                    if (submitItemId > int.MaxValue)
                    {
                        if (!playerItems.TryGetValue(submitItemId, out var itemData))
                        {
                            LogicLog.LogError($"道具提交失败，submit cid: {cid}, 通过提交的道具id {submitItemId} 找不到任何道具。");
                            return false;
                        }
                        submitItemCid = itemData.cid;
                        submitItemNum = itemData.num;
                    }
                    else
                    {
                        submitItemCid = (int)submitItemId;
                        if (playerBasicItems.TryGetValue(submitItemCid, out int num))
                        {
                            submitItemNum = num;
                        }
                    }
                    //如果要求顺序，id不相等则不匹配
                    if (cfgItemSubmit.Type == ItemSubmitType.Ordered && requireItemCid != submitItemCid)
                    {
                        isMatch = false;
                        break;
                    }
                    //如果不要求顺序，id不存在于集合中就不匹配
                    else if (cfgItemSubmit.Type == ItemSubmitType.Unordered && !matchItemCids.Contains(submitItemCid))
                    {
                        isMatch = false;
                        break;
                    }
                    //id验证通过，移除验证过的id，避免同一个道具反复判断
                    matchItemCids.Remove(submitItemCid);
                    //验证数量
                    if (submitItemNum < requireItemNum)
                    {
                        LogicLog.LogError($"道具提交失败，submit cid: {cid}, 道具 {submitItemId} 数量不够，客户端未拦截。");
                        return false;
                    }
                }

                //外层循环，如果内部有一个judge匹配，则验证成功，推出循环
                if (isMatch)
                {
                    matchedJudge = cfgItemJudge;
                    break;
                }
            }

            //提交失败，但不扣道具，直接返回
            if (matchedJudge == null && !cfgItemSubmit.IsFailCostItem)
            {
                ReleaseItemSubmitOccupation(player, triggerEntity);
                return true;
            }

            //计算消耗
            List<int> itemIdNums = ListPool<int>.Get();
            foreach (var itemId in submittedItemList)
            {
                int itemCid = 0;
                if (itemId <= int.MaxValue)
                {
                    itemCid = (int)itemId;
                }
                else if (playerItems.TryGetValue(itemId, out var itemData))
                {
                    itemCid = itemData.cid;
                }
                int itemNum = cfgItemSubmit.AllowItem[itemCid];
                itemIdNums.Add(itemCid);
                itemIdNums.Add(itemNum);
            }

            //提交失败
            if (matchedJudge == null)
            {
                LevelServiceModule.TriggerSystemServices(this, player, triggerEntity, cfgItemSubmit.FailServices);
                if (cfgItemSubmit.IsFailCostItem)
                {
                    LogicItemModule.Instance.CostItem(player.logicPlayer, itemIdNums, LgLevelReasonId.ItemSubmitFail);
                }
                ListPool<int>.Put(itemIdNums);
                ReleaseItemSubmitOccupation(player, triggerEntity);
                return true;
            }
            //提交成功
            LevelServiceModule.TriggerSystemServices(this, player, triggerEntity, matchedJudge.SuccessServices);
            if (cfgItemSubmit.IsSuccessCostItem)
            {
                LogicItemModule.Instance.CostItem(player.logicPlayer, itemIdNums, LgLevelReasonId.ItemSubmitSuccess);
            }
            ListPool<int>.Put(itemIdNums);
            ReleaseItemSubmitOccupation(player, triggerEntity);
            isSuccess = true;
            return true;
        }

        private void ReleaseItemSubmitOccupation(LevelPlayer player, LevelEntity triggerEntity)
        {
            player.entity.ReleaseOccupation(EntityOccupationStatus.ItemSubmit);
            triggerEntity.ReleaseOccupation(EntityOccupationStatus.ItemSubmit);
        }

        public override IPOD GeneratePOD()
        {
            LevelRegionPOD pod = new LevelRegionPOD();
            pod.cid = CfgRegionInstance.Id;
            //pod.Entities = null; //实体列表在确定玩家全部进入后，通过aoi逻辑计算，详情参看Level.TryBroadcastEnterLevel()
            pod.unsafe_effectCids = effectIds?.Clone();
            pod.customBgmCid = customBgmCid;
            pod.unsafe_customWeathers = customWeathers?.Clone();
            return pod;
        }

        public List<LevelEntityPOD> CreateToSaveEntityPODs()
        {
            List<LevelEntityPOD> list = new List<LevelEntityPOD>();
            for (int i = 0; i < entities.Count; i++)
            {
                LevelEntity entity = entities[i];
                list.Add(entity.GeneratePOD(true, true) as LevelEntityPOD);
            }
            return list;
        }

        public int ChangeManualTrackingEntity(LevelPlayer player, int entityId)
        {
            LevelEntity entity = null;
            if (entityId != 0 && !entityMap.TryGetValue(entityId, out entity))
            {
                return ErrorCode.InvalidEntity;
            }
            player.manualTrackingEntity = entity;
            //强制刷新一次aoi，保证客户端可以获取到实体并显示跟踪信息
            RefreshPlayerAOIEntities(player, false);
            return ErrorCode.SUCCESS;
        }

        public bool EndPushEntity(LevelPlayer player, int entityId, int x, int y, int z)
        {
            LevelEntity entity = null;
            if (entityId != 0 && !entityMap.TryGetValue(entityId, out entity))
            {
                LogicLog.LogError($"实体 [{entityId}] 不存在。");
                return false;
            }
            if (!entity.TryGetOccupation(EntityOccupationStatus.BeingPush, out object objOccupationParam))
            {
                LogicLog.LogError($"实体 [{entityId}] 不处于被推动的状态。");
                return false;
            }

            //校验
            var entityPos2D = new Vector2Logic(entity.position.x, entity.position.z);
            var farthestEndPos = (Vector2Logic)objOccupationParam;
            var targetPos2D = new Vector2Logic(x, z);

            if (entity.position.y - y > 100)
            {
                LogicLog.LogError($"实体 [{entityId}] 到目标点 [{x}, {y}, {z}] 的 高度和起始点比差异过大。");
                return false;
            }

            var eToTarget = targetPos2D - entityPos2D;
            var eToFarthestPos = farthestEndPos - entityPos2D;

            if (Vector2Logic.Dot(eToTarget, eToFarthestPos) <= 0)
            {
                LogicLog.LogError($"实体 [{entityId}] 到目标点 [{x}, {y}, {z}] 的 角度和预计偏差过大。");
                return false;
            }
            if (eToTarget.sqrMagnitude > eToFarthestPos.sqrMagnitude)
            {
                LogicLog.LogError($"实体 [{entityId}] 到目标点 [{x}, {y}, {z}] 的 距离超过指定的最大距离。");
                return false;
            }

            if (!entity.MoveInNavMesh(new Vector3Logic(x, y, z), 1, false, EntityMoveDirType.KeepRotation))
            {
                LogicLog.LogError($"实体 [{entityId}] 无法到达位置 [{x}, {y}, {z}]。");
                return false;
            }
            entity.EndOccupationAction(player, EntityOccupationStatus.BeingPush);
            return true;
        }

        /// <summary>
        /// 切换游戏位面
        /// </summary>
        /// <param name="player"></param>
        /// <param name="phasing"></param>
        public bool SwitchPhasing(LevelPlayer player, GameScenePhasing phasing, Vector3Logic mainPhasingPos)
        {
            //位面没变，直接返回切换失败
            if (phasing == player.gameScenePhasing)
            {
                return false;
            }
            //回到主位面时，如果传了位置参数则需要移动过去（注意不能传送，因为只有移动会校验是否可达）
            if (phasing == GameScenePhasing.Main && mainPhasingPos != Vector3Logic.zero)
            {
                if (player.entity.isOccupied)
                {
                    LogicLog.LogError("角色处于占用状态无法位移。");
                    return false;
                }
                if (!Move(player, mainPhasingPos, false))
                {
                    LogicLog.LogError("切换位面后位移失败。");
                    return false;
                }
            }
            //校验通过开始切换位面
            player.gameScenePhasing = phasing;
            //切换到主位面之外的其他位面，aoi可能会变，这里强制刷新一次
            switch (phasing)
            {
                //切回主位面重新刷新aoi
                case GameScenePhasing.Main:
                    RefreshPlayerAOIEntities(player, true);
                    break;
                //切到客户端玩法/剧情位面，清空所有aoi对象
                case GameScenePhasing.ClientPlay:
                    ClearAllAOIEntitiesAndNotify(player);
                    break;
                default:
                    LogicLog.LogError("暂不支持位面类型：" + phasing.ToString());
                    return false;
            }
            return true;
        }

        public override void OnReset()
        {
            base.OnReset();
            _aoiUpdateFrame = 0;
            foreach (LevelEntity entity in entities)
            {
                ClassPool.Put(entity);
            }
            entities.Clear();
            entities = null;
            entityMap.Clear();
            entityMap = null;
            players.Clear();//list element(LevelPlayer) Dispose by level
            players = null;
            terrain.Dispose();
            terrain = null;
            _cacheMcidEntitys.Clear();
            _execNavEntityMap.Clear();
            customWeathers?.Clear();
            customWeathers = null;
            _areaEntities.Clear();
            _runningElevators.Clear();
            gameplayConfig = null;
            CfgRegionInstance = null;
            level = null;
            _checkPoints.Clear();
            _bornPointEntity = null;
            _elevatorUpdateFrame = 0;
            effectIds = null;
        }
    }
}
